/*
 * Copyright 2019-2029 geekidea(https://github.com/geekidea)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.ld.security.auth.shiro.config;

import com.alibaba.fastjson.JSON;
import com.ld.security.auth.common.sys.login.UserService;
import com.ld.security.auth.shiro.exception.ShiroConfigException;
import com.ld.security.auth.shiro.login.jwt.JwtCredentialsMatcher;
import com.ld.security.auth.shiro.login.jwt.JwtFilter;
import com.ld.security.auth.shiro.login.jwt.JwtProperties;
import com.ld.security.auth.shiro.login.jwt.JwtRealm;
import com.ld.security.auth.shiro.login.jwt.service.JwtLoginService;
import com.ld.security.auth.shiro.login.jwt.service.impl.JwtLoginServiceImpl;
import com.ld.security.auth.shiro.login.pass.PasswordCredentialsMatcher;
import com.ld.security.auth.shiro.login.pass.PasswordRealm;
import com.ld.security.auth.shiro.login.pass.PasswordService;
import com.ld.security.auth.shiro.login.pass.impl.PasswordServiceImpl;
import com.ld.security.common.core.util.IniUtil;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.Authenticator;
import org.apache.shiro.authc.pam.FirstSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SessionStorageEvaluator;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.DefaultSessionManager;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSessionStorageEvaluator;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.web.filter.DelegatingFilterProxy;

import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Order(5)
@Slf4j
@Configuration
public class JwtShiroConfig {

  /** JWT过滤器名称 */
  private static final String JWT_FILTER_NAME = "jwtFilter";
  /** Shiro过滤器名称 */
  private static final String SHIRO_FILTER_NAME = "shiroFilter";


  @Bean
  public JwtCredentialsMatcher jwtCredentialsMatcher() {
    return new JwtCredentialsMatcher();
  }

  @Bean
  public PasswordCredentialsMatcher passwordCredentialsMatcher() {
    return new PasswordCredentialsMatcher();
  }


  @Bean
  public PasswordRealm passwordRealm(UserService userService) {
    PasswordRealm realm =  new PasswordRealm();
    realm.setUserAuthService(userService);
    realm.setCredentialsMatcher(passwordCredentialsMatcher());
    return realm;
  }

  /**
   * JWT数据源验证
   *
   * @return
   */
  @Bean
  public JwtRealm jwtRealm(UserService userService) {
    JwtRealm jwtRealm = new JwtRealm();
    jwtRealm.setUserAuthService(userService);
    jwtRealm.setCachingEnabled(false);
    jwtRealm.setCredentialsMatcher(jwtCredentialsMatcher());
    return jwtRealm;
  }


  @Bean
  //cookie用
  public SessionDAO sessionDAO() {
    return new EnterpriseCacheSessionDAO();
  }

  /**
   * 禁用session
   *
   * @return
   */
  @Bean
  public DefaultSessionManager sessionManager(ShiroProperties shiroProperties) {
    if(shiroProperties.isOpenCookie()){
      DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();

      //redisTemplate默认采用的其实是valueSerializer，就算是采用其他ops也一样，这是一个坑。
      //这个name的作用也不大，只是有特色的cookie的名称。
      sessionManager.setDeleteInvalidSessions(true);
      sessionManager.setSessionDAO(sessionDAO());
      sessionManager.setSessionIdCookieEnabled(true);
      SimpleCookie cookie = new SimpleCookie();
      cookie.setName("session_id");
      sessionManager.setSessionIdCookie(cookie);
      sessionManager.setSessionIdCookieEnabled(true);

      return sessionManager;
    } else {
      DefaultSessionManager manager = new DefaultSessionManager();
      manager.setSessionValidationSchedulerEnabled(false);

      return manager;
}
    }





  @Bean
  public SessionStorageEvaluator sessionStorageEvaluator() {
    DefaultSessionStorageEvaluator sessionStorageEvaluator =
        new DefaultWebSessionStorageEvaluator();
    sessionStorageEvaluator.setSessionStorageEnabled(false);
    return sessionStorageEvaluator;
  }



  @Bean
  public PasswordService passwordService() {
    PasswordService passwordService = new PasswordServiceImpl() ;
    return passwordService;
  }

  @Bean
  public JwtLoginService jwtLoginService() {
    JwtLoginService jwtLoginService = new JwtLoginServiceImpl() ;
    return jwtLoginService;
  }


  @Bean
  public DefaultSubjectDAO subjectDAO() {
    DefaultSubjectDAO defaultSubjectDAO = new DefaultSubjectDAO();

    defaultSubjectDAO.setSessionStorageEvaluator(sessionStorageEvaluator());
    return defaultSubjectDAO;
  }

  public List<Realm> realms(UserService userService){
    return Arrays.asList(this.jwtRealm(userService),this.passwordRealm(userService));
  }



  /**
   * 安全管理器配置
   *
   * @return
   */
  @Bean
  public DefaultWebSecurityManager securityManager(UserService userService, ShiroProperties shiroProperties) {
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    securityManager.setRealm(jwtRealm(userService));
    securityManager.setAuthenticator(this.authenticator(userService));
    securityManager.setSubjectDAO(subjectDAO());
    securityManager.setSessionManager(sessionManager(shiroProperties));
    SecurityUtils.setSecurityManager(securityManager);


    return securityManager;
  }

  /**
   * ShiroFilterFactoryBean配置
   *
   * @param securityManager
   * @param
   * @param shiroProperties
   * @param jwtProperties
   * @return
   */
  @Bean(SHIRO_FILTER_NAME)
  public ShiroFilterFactoryBean shiroFilterFactoryBean(
          DefaultWebSecurityManager securityManager,
      JwtLoginService jwtLoginService,
      ShiroProperties shiroProperties,
      JwtProperties jwtProperties) {
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    shiroFilterFactoryBean.setSecurityManager(securityManager);
    Map<String, Filter> filterMap = new HashMap<>();
    filterMap.put(JWT_FILTER_NAME, new JwtFilter(jwtLoginService, jwtProperties));
    shiroFilterFactoryBean.setFilters(filterMap);
    Map<String, String> filterChainMap =
        shiroFilterChainDefinition(shiroProperties).getFilterChainMap();
    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);
    return shiroFilterFactoryBean;
  }

  /**
   * Shiro路径权限配置
   *
   * @return
   */
  @Bean
  public ShiroFilterChainDefinition shiroFilterChainDefinition(ShiroProperties shiroProperties) {
    DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
    // 获取ini格式配置
    String definitions = shiroProperties.getFilterChainDefinitions();
    if (StringUtils.isNotBlank(definitions)) {
      Map<String, String> section = IniUtil.parseIni(definitions);
      log.debug("definitions:{}", JSON.toJSONString(section));
      for (Map.Entry<String, String> entry : section.entrySet()) {
        chainDefinition.addPathDefinition(entry.getKey(), entry.getValue());
      }
    }

    // 获取自定义权限路径配置集合
    List<ShiroPermissionConfig> permissionConfigs = shiroProperties.getPermissionConfig();
    log.debug("permissionConfigs:{}", JSON.toJSONString(permissionConfigs));
    if (!permissionConfigs.isEmpty()) {
      for (ShiroPermissionConfig permissionConfig : permissionConfigs) {
        String url = permissionConfig.getUrl();
        String[] urls = permissionConfig.getUrls();
        String permission = permissionConfig.getPermission();
        if (StringUtils.isBlank(url) && ArrayUtils.isEmpty(urls)) {
          throw new ShiroConfigException("shiro permission config 路径配置不能为空");
        }
        if (StringUtils.isBlank(permission)) {
          throw new ShiroConfigException("shiro permission config permission不能为空");
        }

        if (StringUtils.isNotBlank(url)) {
          chainDefinition.addPathDefinition(url, permission);
        }
        if (ArrayUtils.isNotEmpty(urls)) {
          for (String string : urls) {
            chainDefinition.addPathDefinition(string, permission);
          }
        }
      }
    }
    // 最后一个设置为JWTFilter
    //chainDefinition.addPathDefinition("/**", JWT_FILTER_NAME);

    Map<String, String> filterChainMap = chainDefinition.getFilterChainMap();
    log.debug("filterChainMap:{}", JSON.toJSONString(filterChainMap));

    return chainDefinition;
  }

  /**
   * ShiroFilter配置
   *
   * @return
   */
  @Bean
  public FilterRegistrationBean delegatingFilterProxy() {
    FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
    DelegatingFilterProxy proxy = new DelegatingFilterProxy();
    proxy.setTargetFilterLifecycle(true);
    proxy.setTargetBeanName(SHIRO_FILTER_NAME);
    filterRegistrationBean.setFilter(proxy);
    filterRegistrationBean.setAsyncSupported(true);
    filterRegistrationBean.setEnabled(true);
    filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC);
    return filterRegistrationBean;
  }

  @Bean
  public Authenticator authenticator(UserService userService) {
    ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
    authenticator.setRealms(this.realms(userService));
    authenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy());
    return authenticator;
  }

  /**
   * Enabling Shiro Annotations
   *
   * @return
   */
  @Bean
  public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
    return new LifecycleBeanPostProcessor();
  }
//  注解权限关闭
//  /**
//   * depends-on lifecycleBeanPostProcessor
//   *
//   * @return
//   */
//  @Bean
//  public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
//    DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator =
//        new DefaultAdvisorAutoProxyCreator();
//    return defaultAdvisorAutoProxyCreator;
//  }
//
//  @Bean
//  public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
//    AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =
//        new AuthorizationAttributeSourceAdvisor();
//    authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
//    return authorizationAttributeSourceAdvisor;
//  }
}